/**
 * 
 */
package org.gaming.common.service;

import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

import org.gaming.db.annotation.Table;
import org.gaming.db.repository.BaseRepository;
import org.gaming.db.usecase.SlimDao;
import org.gaming.ruler.redis.RedisCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * @author YY
 *
 */
public abstract class PlayerOneToOneService<T extends PlayerOneToOneEntity> {

	protected static Logger logger = LoggerFactory.getLogger(PlayerOneToOneService.class);
	
	@Autowired
	protected RedisCache redisCache;
	
	private final Class<T> clazz;
	private BaseRepository<T> repository;
	
	private static List<PlayerOneToOneService<?>> playerOneToOneServices = new ArrayList<>();
	
	@SuppressWarnings("unchecked")
	public PlayerOneToOneService() {
		this.clazz = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
		
		playerOneToOneServices.add(this);
	}
	
	private BaseRepository<T> repository() {
		if(repository == null) {
			repository = SlimDao.getRepository(clazz);
		}
		return repository;
	}
	
	private void newWhenPlayerCreate(long playerId) {
		T entity = createWhenNull(playerId);
		if(entity != null) {
			entity.setPlayerId(playerId);
			entity.setDbZone(currDbZone());
			this.insert(entity);
		}
	}
	
	/**
	 * 获取玩家数据
	 * @param playerId
	 * @return
	 */
	public T getEntity(long playerId) {
		//先从缓存中获取
		String redisKey = redisKey(playerId);
		T entity = redisCache.getValue(redisKey, clazz);
		if(entity != null) {
			ensureSameZone(entity);
			redisCache.setExpire(redisKey, RedisCache.TIMEOUT);
			return entity;
		}
		//缓存中没有则查询数据库
		entity = repository().getByMainKey(playerId);
		if(entity != null) {
			ensureSameZone(entity);
			redisCache.updateValueWillTimeout(redisKey, entity);
			return entity;
		}
		//数据库中没有则进行插入
		entity = createWhenNull(playerId);
		if(entity != null) {
			entity.setPlayerId(playerId);
			entity.setDbZone(currDbZone());
			this.insert(entity);
			redisCache.updateValueWillTimeout(redisKey, entity);
		}
		return entity;
	}
	/**
	 * 查询为null时创建对应的数据对象
	 * @param playerId
	 * @param dbZone
	 * @return
	 */
	protected abstract T createWhenNull(long playerId);
	
	public final void insert(T entity) {
		ensureSameZone(entity);
		repository().insert(entity);
		
		String redisKey = redisKey(entity.getPlayerId());
		redisCache.updateValueWillTimeout(redisKey, entity);
	}
	
	public final void update(T entity) {
		ensureSameZone(entity);
		repository().update(entity);
		
		String redisKey = redisKey(entity.getPlayerId());
		redisCache.updateValueWillTimeout(redisKey, entity);
	}
	
	private String tableName;
	private final String tableName() {
		if(tableName == null) {
			Table table = this.clazz.getAnnotation(Table.class);
			tableName = table.name();
		}
		return tableName;
	}
	
	private final String redisKey(long playerId) {
		return "player:" + playerId + ":" + tableName();
	}
	
	public static void createPlayerDatas(long playerId) {
		for(PlayerOneToOneService<?> service : playerOneToOneServices) {
			service.newWhenPlayerCreate(playerId);
		}
	}
	
	private void ensureSameZone(T entity) {
		if(entity.getDbZone() != currDbZone()) {
			throw new RuntimeException(String.format("玩家%s的个人数据%s不应该在当前游戏服中被操作，归属数据库%s，当前数据库%s", entity.getPlayerId(),
					entity.getClass().getSimpleName(), entity.getDbZone(), currDbZone()));
		}
	}
	
	protected abstract int currDbZone();
}
